package com.leyou.search.service;


import com.fasterxml.jackson.core.type.TypeReference;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exception.LyException;
import com.leyou.common.utils.JsonUtils;
import com.leyou.common.vo.PageResult;
import com.leyou.item.pojo.*;
import com.leyou.search.client.BrandClient;
import com.leyou.search.client.CategoryClient;
import com.leyou.search.client.GoodsClient;
import com.leyou.search.client.SpecificationClient;
import com.leyou.search.pojo.Goods;
import com.leyou.search.pojo.SearchRequest;
import com.leyou.search.pojo.SearchResult;
import com.leyou.search.repository.GoodsRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SearchService {

    @Autowired
    private BrandClient brandClient;

    @Autowired
    private CategoryClient categoryClient;

    @Autowired
    private GoodsClient goodsClient;

    @Autowired
    private SpecificationClient specificationClient;

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    public Goods bulidGoods(Spu spu) {
        //查询分类
        List<Category> categories = categoryClient.queryCategoryByIds(
                Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3())
        );
        if (CollectionUtils.isEmpty(categories)) {
            throw new LyException(ExceptionEnum.CATEGORY_NOT_FOND);
        }
        List<String> collect = categories.stream()
                .map(Category::getName)
                .collect(Collectors.toList());
        //查询品牌
        Brand brand = brandClient.queryBrandListByid(spu.getBrandId());
        if (brand == null) {
            throw new LyException(ExceptionEnum.BRAND_NOT_FOUND);
        }
        //搜索字段
        String all = spu.getTitle() + StringUtils.join(collect, " ") + brand.getName();

        //查询SKU
        List<Sku> skusList = goodsClient.querySkuBySpuId(spu.getId());
        if (CollectionUtils.isEmpty(skusList)) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        //对sku处理
        List<Map<String, Object>> skus = new ArrayList<>();
        //价格集合
        Set<Long> priceList = new HashSet<>();

        for (Sku sku : skusList) {
            Map<String, Object> map = new HashMap<>();
            map.put("id", sku.getId());
            map.put("title", sku.getTitle());
            map.put("price", sku.getPrice());
            map.put("images", StringUtils.substringBefore(sku.getImages(), ","));
            skus.add(map);

            //处理价格
            priceList.add(sku.getPrice());
        }
//查询规格插参数
        List<SpecParam> specParams = specificationClient.querySpecificationList(null, spu.getCid3(), true);
        if (CollectionUtils.isEmpty(specParams)) {
            throw new LyException(ExceptionEnum.SPEC_GROUP_NOT_FOUND);
        }
        SpuDetail spuDetail = goodsClient.detailById(spu.getId());
        //获取通用规格参数
        Map<Long, String> genericSpec = JsonUtils.toMap(spuDetail.getGenericSpec(), Long.class, String.class);
        //获取特有规格参数
        Map<Long, List<Object>> spcialSpec = JsonUtils.nativeRead(spuDetail.getSpecialSpec(), new TypeReference<Map<Long, List<Object>>>() {
        });
        //规格参数,key是规格参数的名字,值是值
        HashMap<String, Object> spec = new HashMap<>();
        for (SpecParam specParam : specParams) {
            String key = specParam.getName();
            Object value;
            if (specParam.getGeneric()) {
                value = genericSpec.get(specParam.getId());
                //判断是否是数值类型
                if (specParam.getNumeric()) {
                    //处理成段
                    value = chooseSegment(value.toString(), specParam);
                }
            } else {
                value = spcialSpec.get(specParam.getId());
            }
            //存入map
            spec.put(key, value);
        }
        //构建GOODS
        Goods goods = new Goods();
        goods.setBrandId(spu.getBrandId());
        goods.setCid1(spu.getCid1());
        goods.setCid2(spu.getCid2());
        goods.setCid3(spu.getCid3());
        goods.setCreateTime(spu.getCreateTime());
        goods.setId(spu.getId());
        goods.setAll(all);//  2019/1/5  搜索字段,标题,价格
        goods.setPrice(priceList);// 2019/1/5 所有sku的价格
        goods.setSkus(JsonUtils.toString(skus));// 2019/1/5  所有skus的规格参数
        goods.setSpecs(spec);// 2019/1/5  所有索搜的规格参数
        goods.setSubTitle(spu.getSubTitle());
        return goods;
    }

    private String chooseSegment(String value, SpecParam p) {
        double val = NumberUtils.toDouble(value);
        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = NumberUtils.toDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if (segs.length == 2) {
                end = NumberUtils.toDouble(segs[1]);
            }
            // 判断是否在范围内
            if (val >= begin && val < end) {
                if (segs.length == 1) {
                    result = segs[0] + p.getUnit() + "以上";
                } else if (begin == 0) {
                    result = segs[1] + p.getUnit() + "以下";
                } else {
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }

    public PageResult<Goods> search(SearchRequest request) {
        Integer page = request.getPage() - 1;
        Integer size = request.getSize();
        //创建查询构造器
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //0.结果过滤
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id", "subTitle", "skus"}, null));
        //1.分页
        queryBuilder.withPageable(PageRequest.of(page, size));
        //2.过滤
        QueryBuilder basicQuery = buildBasicQuery(request);
        queryBuilder.withQuery(basicQuery);
        //集合分类和品牌
        //3.1聚合分类
        String categoryAggname = "category_agg";
        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAggname).field("cid3"));
        //3.1聚合品牌
        String brandAggname = "brand_agg";
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAggname).field("brandId"));
        //4.查询
        Page<Goods> result = elasticsearchTemplate.queryForPage(queryBuilder.build(), Goods.class);
        //5.分析结果
        //总结果
        long total = result.getTotalElements();
        //总页数
        int totalPages = result.getTotalPages();
        //当前页结果
        List<Goods> goodsList = result.getContent();
        //5.1解析聚合结果
        Aggregations aggregations = ((AggregatedPage<Goods>) result).getAggregations();
        List<Category> categories = parseCategoryAgg(aggregations.get(categoryAggname));
        List<Brand> brands = parseBrandAgg(aggregations.get(brandAggname));

        //6:完成规格参数查询

        List<Map<String, Object>> specs = null;

        if (categories != null && categories.size() == 1) {
            //商品可以聚合
            specs = builSpecificationAgg(categories.get(0).getId(), basicQuery);
        }
        return new SearchResult(total, totalPages, goodsList, categories, brands, specs);

    }

    private QueryBuilder buildBasicQuery(SearchRequest request) {
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        //查询条件
        queryBuilder.must(QueryBuilders.matchQuery("all", request.getKey()));
        //过滤调价
        Map<String, String> map = request.getFilter();
        for (Map.Entry<String,String> entry : map.entrySet()){
            String key = entry.getKey();
            //处理key
            if("cide3".equals(key) && !"bandId".equals(key)){
                key = "specs"+key+".keyword";
            }
            String value = entry.getValue();
            queryBuilder.filter(QueryBuilders.termQuery(key,value));
        }
        return null;
    }

    private List<Map<String, Object>> builSpecificationAgg(Long cid, QueryBuilder basicQuery) {
        List<Map<String, Object>> specs = new ArrayList<>();

        //1需要聚合的规格参数
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        List<SpecParam> params = specificationClient.querySpecificationList(null, cid, true);
        //2 聚合
        queryBuilder.withQuery(basicQuery);
        for (SpecParam param : params) {
            String name = param.getName();
            queryBuilder.addAggregation(AggregationBuilders.terms(name).field("specs."+name+".keyword"));
        }
        //3 获取结果
        AggregatedPage<Goods> result = elasticsearchTemplate.queryForPage(queryBuilder.build(), Goods.class);
        //4解析结果
        Aggregations aggs = result.getAggregations();
        for (SpecParam param : params) {
            String name = param.getName();
            StringTerms terms = aggs.get(name);
            List<String> options = terms.getBuckets()
                    .stream().map(b -> b.getKeyAsString())
                    .collect(Collectors.toList());
        //准备map
            Map<String, Object> hashMap = new HashMap<>();
            hashMap.put("k",name);
            hashMap.put("options",options);
            specs.add(hashMap);
        }
        return specs;
    }

    private List<Brand> parseBrandAgg(LongTerms aggregation) {
        try {
            List<Long> collect = aggregation.getBuckets()
                    .stream()
                    .map(b -> b.getKeyAsNumber().longValue())
                    .collect(Collectors.toList());
            List<Brand> brands = brandClient.queryBrandByids(collect);
            return brands;
        } catch (Exception e) {
            log.error("查询失败Brand");
            return null;
        }
    }

    private List<Category> parseCategoryAgg(LongTerms aggregation) {
        try {
            List<Long> collect = aggregation.getBuckets()
                    .stream()
                    .map(b -> b.getKeyAsNumber().longValue())
                    .collect(Collectors.toList());
            List<Category> categories = categoryClient.queryCategoryByIds(collect);
            return categories;
        } catch (Exception e) {
            log.error("查询失败Category");
            return null;
        }

    }
}
